home *** CD-ROM | disk | FTP | other *** search
/ PC Format (PL) 2008 February / PC_Format_022008.iso / Internet / Mozilla Thunderbird wtyczki / lightning-0.7-tb-win.xpi / components / calMemoryCalendar.js < prev    next >
Encoding:
Text File  |  2007-09-22  |  22.1 KB  |  610 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  *  Oracle Corporation
  19.  * Portions created by the Initial Developer are Copyright (C) 2004
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. //
  40. // calMemoryCalendar.js
  41. //
  42.  
  43. const calCalendarManagerContractID = "@mozilla.org/calendar/manager;1";
  44. const calICalendarManager = Components.interfaces.calICalendarManager;
  45.  
  46. const USECS_PER_SECOND = 1000000;
  47.  
  48. function calMemoryCalendar() {
  49.     this.wrappedJSObject = this;
  50.     this.calendarToReturn = this,
  51.     this.initMemoryCalendar();
  52. }
  53.  
  54. // END_OF_TIME needs to be the max value a PRTime can be
  55. const START_OF_TIME = -0x7fffffffffffffff;
  56. const END_OF_TIME = 0x7fffffffffffffff;
  57.  
  58. calMemoryCalendar.prototype = {
  59.     // This will be returned from getItems as the calendar. The ics
  60.     // calendar overwrites this.
  61.     calendarToReturn: null,
  62.     mObservers: null,
  63.  
  64.     //
  65.     // nsISupports interface
  66.     // 
  67.     QueryInterface: function (aIID) {
  68.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  69.             !aIID.equals(Components.interfaces.calICalendarProvider) &&
  70.             !aIID.equals(Components.interfaces.calICalendar)) {
  71.             throw Components.results.NS_ERROR_NO_INTERFACE;
  72.         }
  73.  
  74.         return this;
  75.     },
  76.  
  77.     initMemoryCalendar: function() {
  78.         this.mObservers = new calListenerBag(Components.interfaces.calIObserver);
  79.         this.mItems = { };
  80.     },
  81.  
  82.     //
  83.     // calICalendarProvider interface
  84.     //
  85.     get prefChromeOverlay() {
  86.         return null;
  87.     },
  88.  
  89.     get displayName() {
  90.         return calGetString("calendar", "memoryName");
  91.     },
  92.  
  93.     createCalendar: function mem_createCal() {
  94.         throw NS_ERROR_NOT_IMPLEMENTED;
  95.     },
  96.  
  97.     deleteCalendar: function mem_deleteCal(cal, listener) {
  98.         cal = cal.wrappedJSObject;
  99.         cal.mItems = {};
  100.  
  101.         try {
  102.             listener.onDeleteCalendar(cal, Components.results.NS_OK, null);
  103.         } catch(ex) {}
  104.     },
  105.  
  106.     //
  107.     // calICalendar interface
  108.     //
  109.  
  110.     // attribute AUTF8String name;
  111.     // attribute AUTF8String id;
  112.     mID: null,
  113.     get id() {
  114.         return this.mID;
  115.     },
  116.     set id(id) {
  117.         if (this.mID)
  118.             throw Components.results.NS_ERROR_ALREADY_INITIALIZED;
  119.         return (this.mID = id);
  120.     },
  121.  
  122.     get name() {
  123.         return getCalendarManager().getCalendarPref(this, "NAME");
  124.     },
  125.     set name(name) {
  126.         getCalendarManager().setCalendarPref(this, "NAME", name);
  127.     },
  128.  
  129.     // readonly attribute AUTF8String type;
  130.     get type() { return "memory"; },
  131.  
  132.     mReadOnly: false,
  133.  
  134.     // Most of the time you can just let the ics calendar handle this
  135.     get readOnly() { 
  136.         return this.mReadOnly;
  137.     },
  138.     set readOnly(bool) {
  139.         this.mReadOnly = bool;
  140.     },
  141.  
  142.     get canRefresh() {
  143.         return false;
  144.     },
  145.  
  146.     // attribute nsIURI uri;
  147.     mUri: null,
  148.     get uri() { return this.mUri; },
  149.     set uri(aURI) { this.mUri = aURI; },
  150.  
  151.  
  152.     refresh: function() {
  153.         // no-op
  154.     },
  155.  
  156.     // attribute boolean suppressAlarms;
  157.     get suppressAlarms() { return false; },
  158.     set suppressAlarms(aSuppressAlarms) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; },
  159.  
  160.     get sendItipInvitations() { return true; },
  161.  
  162.     // void addObserver( in calIObserver observer );
  163.     addObserver: function (aObserver) {
  164.         this.mObservers.add(aObserver);
  165.     },
  166.  
  167.     // void removeObserver( in calIObserver observer );
  168.     removeObserver: function (aObserver) {
  169.         this.mObservers.remove(aObserver);
  170.     },
  171.  
  172.     // void addItem( in calIItemBase aItem, in calIOperationListener aListener );
  173.     addItem: function (aItem, aListener) {
  174.         var newItem = aItem.clone();
  175.         return this.adoptItem(newItem, aListener);
  176.     },
  177.     
  178.     // void adoptItem( in calIItemBase aItem, in calIOperationListener aListener );
  179.     adoptItem: function (aItem, aListener) {
  180.         if (this.readOnly) 
  181.             throw Components.interfaces.calIErrors.CAL_IS_READONLY;
  182.         if (aItem.id == null && aItem.isMutable)
  183.             aItem.id = getUUID();
  184.  
  185.         if (aItem.id == null) {
  186.             if (aListener)
  187.                 aListener.onOperationComplete (this.calendarToReturn,
  188.                                                Components.results.NS_ERROR_FAILURE,
  189.                                                aListener.ADD,
  190.                                                aItem.id,
  191.                                                "Can't set ID on non-mutable item to addItem");
  192.             return;
  193.         }
  194.  
  195.         if (this.mItems[aItem.id] != null) {
  196.             // is this an error?
  197.             if (aListener)
  198.                 aListener.onOperationComplete (this.calendarToReturn,
  199.                                                Components.interfaces.calIErrors.DUPLICATE_ID,
  200.                                                aListener.ADD,
  201.                                                aItem.id,
  202.                                                "ID already exists for addItem");
  203.             return;
  204.         }
  205.  
  206.         aItem.calendar = this.calendarToReturn;
  207.         var rec = aItem.recurrenceInfo;
  208.         if (rec) {
  209.             var exceptions = rec.getExceptionIds({});
  210.             for each (var exid in exceptions) {
  211.                 var exception = rec.getExceptionFor(exid, false);
  212.                 if (exception) {
  213.                     if (!exception.isMutable) {
  214.                         exception = exception.clone();
  215.                     }
  216.                     exception.calendar = this.calendarToReturn;
  217.                     rec.modifyException(exception);
  218.                 }
  219.             }
  220.         }
  221.         
  222.         aItem.generation = 1;
  223.         aItem.makeImmutable();
  224.         this.mItems[aItem.id] = aItem;
  225.  
  226.         // notify the listener
  227.         if (aListener)
  228.             aListener.onOperationComplete (this.calendarToReturn,
  229.                                            Components.results.NS_OK,
  230.                                            aListener.ADD,
  231.                                            aItem.id,
  232.                                            aItem);
  233.         // notify observers
  234.         this.mObservers.notify("onAddItem", [aItem]);
  235.     },
  236.  
  237.     // void modifyItem( in calIItemBase aNewItem, in calIItemBase aOldItem, in calIOperationListener aListener );
  238.     modifyItem: function (aNewItem, aOldItem, aListener) {
  239.         if (this.readOnly) 
  240.             throw Components.interfaces.calIErrors.CAL_IS_READONLY;
  241.         if (!aNewItem) {
  242.             throw Components.results.NS_ERROR_FAILURE;
  243.         }
  244.         if (aNewItem.id == null || this.mItems[aNewItem.id] == null) {
  245.             // this is definitely an error
  246.             if (aListener)
  247.                 aListener.onOperationComplete (this.calendarToReturn,
  248.                                                Components.results.NS_ERROR_FAILURE,
  249.                                                aListener.MODIFY,
  250.                                                aNewItem.id,
  251.                                                "ID for modifyItem doesn't exist, is null, or is from different calendar");
  252.             return;
  253.         }
  254.  
  255.         // do the old and new items match?
  256.         if (aOldItem.id != aNewItem.id) {
  257.             if (aListener)
  258.                 aListener.onOperationComplete (this.calendarToReturn,
  259.                                                Components.results.NS_ERROR_FAILURE,
  260.                                                aListener.MODIFY,
  261.                                                aNewItem.id,
  262.                                                "item ID mismatch between old and new items");
  263.             return;
  264.         }
  265.         
  266.         if (aNewItem.parentItem != aNewItem) {
  267.             aNewItem.parentItem.recurrenceInfo.modifyException(aNewItem);
  268.             aNewItem = aNewItem.parentItem;
  269.         }
  270.         aOldItem = aOldItem.parentItem;
  271.  
  272.         if (!compareItems(this.mItems[aOldItem.id], aOldItem)) {
  273.             if (aListener)
  274.                 aListener.onOperationComplete (this.calendarToReturn,
  275.                                                Components.results.NS_ERROR_FAILURE,
  276.                                                aListener.MODIFY,
  277.                                                aNewItem.id,
  278.                                                "old item mismatch in modifyItem");
  279.             return;
  280.         }
  281.  
  282.         if (aOldItem.generation != aNewItem.generation) {
  283.             if (aListener)
  284.                 aListener.onOperationComplete (this.calendarToReturn,
  285.                                                Components.results.NS_ERROR_FAILURE,
  286.                                                aListener.MODIFY,
  287.                                                aNewItem.id,
  288.                                                "generation mismatch in modifyItem");
  289.             return;
  290.         }
  291.  
  292.         var modifiedItem = aNewItem.clone();
  293.         modifiedItem.generation += 1;
  294.         modifiedItem.makeImmutable();
  295.         this.mItems[aNewItem.id] = modifiedItem;
  296.  
  297.         if (aListener)
  298.             aListener.onOperationComplete (this.calendarToReturn,
  299.                                            Components.results.NS_OK,
  300.                                            aListener.MODIFY,
  301.                                            modifiedItem.id,
  302.                                            modifiedItem);
  303.         // notify observers
  304.         this.mObservers.notify("onModifyItem", [modifiedItem, aOldItem]);
  305.     },
  306.  
  307.     // void deleteItem( in calIItemBase aItem, in calIOperationListener aListener );
  308.     deleteItem: function (aItem, aListener) {
  309.         if (this.readOnly) 
  310.             throw Components.interfaces.calIErrors.CAL_IS_READONLY;
  311.         if (aItem.id == null || this.mItems[aItem.id] == null) {
  312.             if (aListener)
  313.                 aListener.onOperationComplete (this.calendarToReturn,
  314.                                                Components.results.NS_ERROR_FAILURE,
  315.                                                aListener.DELETE,
  316.                                                aItem.id,
  317.                                                "ID is null or is from different calendar in deleteItem");
  318.             return;
  319.         }
  320.  
  321.         var oldItem = this.mItems[aItem.id];
  322.         if (oldItem.generation != aItem.generation) {
  323.             if (aListener)
  324.                 aListener.onOperationComplete (this.calendarToReturn,
  325.                                                Components.results.NS_ERROR_FAILURE,
  326.                                                aListener.DELETE,
  327.                                                aItem.id,
  328.                                                "generation mismatch in deleteItem");
  329.             return;
  330.         }
  331.  
  332.         delete this.mItems[aItem.id];
  333.  
  334.         if (aListener)
  335.             aListener.onOperationComplete (this.calendarToReturn,
  336.                                            Components.results.NS_OK,
  337.                                            aListener.DELETE,
  338.                                            aItem.id,
  339.                                            null);
  340.         // notify observers
  341.         this.mObservers.notify("onDeleteItem", [oldItem]);
  342.     },
  343.  
  344.     // void getItem( in string id, in calIOperationListener aListener );
  345.     getItem: function (aId, aListener) {
  346.         if (!aListener)
  347.             return;
  348.  
  349.         if (aId == null ||
  350.             this.mItems[aId] == null) {
  351.             aListener.onOperationComplete(this.calendarToReturn,
  352.                                           Components.results.NS_ERROR_FAILURE,
  353.                                           aListener.GET,
  354.                                           null,
  355.                                           "IID doesn't exist for getItem");
  356.             return;
  357.         }
  358.  
  359.         var item = this.mItems[aId];
  360.         var iid = null;
  361.  
  362.         if (item instanceof Components.interfaces.calIEvent) {
  363.             iid = Components.interfaces.calIEvent;
  364.         } else if (item instanceof Components.interfaces.calITodo) {
  365.             iid = Components.interfaces.calITodo;
  366.         } else {
  367.             aListener.onOperationComplete (this.calendarToReturn,
  368.                                            Components.results.NS_ERROR_FAILURE,
  369.                                            aListener.GET,
  370.                                            aId,
  371.                                            "Can't deduce item type based on QI");
  372.             return;
  373.         }
  374.  
  375.         aListener.onGetResult (this.calendarToReturn,
  376.                                Components.results.NS_OK,
  377.                                iid,
  378.                                null, 1, [item]);
  379.  
  380.         aListener.onOperationComplete (this.calendarToReturn,
  381.                                        Components.results.NS_OK,
  382.                                        aListener.GET,
  383.                                        aId,
  384.                                        null);
  385.  
  386.     },
  387.  
  388.     // void getItems( in unsigned long aItemFilter, in unsigned long aCount, 
  389.     //                in calIDateTime aRangeStart, in calIDateTime aRangeEnd,
  390.     //                in calIOperationListener aListener );
  391.     getItems: function (aItemFilter, aCount,
  392.                         aRangeStart, aRangeEnd, aListener)
  393.     {
  394.         if (!aListener)
  395.             return;
  396.  
  397.         const calICalendar = Components.interfaces.calICalendar;
  398.         const calIRecurrenceInfo = Components.interfaces.calIRecurrenceInfo;
  399.  
  400.         var itemsFound = Array();
  401.         var startTime = START_OF_TIME;
  402.         var endTime = END_OF_TIME;
  403.         if (aRangeStart)
  404.             startTime = aRangeStart.nativeTime;
  405.         if (aRangeEnd)
  406.             endTime = aRangeEnd.nativeTime;
  407.  
  408.         //
  409.         // filters
  410.         //
  411.  
  412.         // item base type
  413.         var wantEvents = ((aItemFilter & calICalendar.ITEM_FILTER_TYPE_EVENT) != 0);
  414.         var wantTodos = ((aItemFilter & calICalendar.ITEM_FILTER_TYPE_TODO) != 0);
  415.         if(!wantEvents && !wantTodos) {
  416.             // bail.
  417.             aListener.onOperationComplete (this.calendarToReturn,
  418.                                            Components.results.NS_ERROR_FAILURE,
  419.                                            aListener.GET,
  420.                                            null,
  421.                                            "Bad aItemFilter passed to getItems");
  422.             return;
  423.         }
  424.  
  425.         // completed?
  426.         var itemCompletedFilter = ((aItemFilter & calICalendar.ITEM_FILTER_COMPLETED_YES) != 0);
  427.         var itemNotCompletedFilter = ((aItemFilter & calICalendar.ITEM_FILTER_COMPLETED_NO) != 0);
  428.  
  429.         // return occurrences?
  430.         var itemReturnOccurrences = ((aItemFilter & calICalendar.ITEM_FILTER_CLASS_OCCURRENCES) != 0);
  431.  
  432.         // figure out the return interface type
  433.         var typeIID = null;
  434.         if (itemReturnOccurrences) {
  435.             typeIID = Components.interfaces.calIItemBase;
  436.         } else {
  437.             if (wantEvents && wantTodos) {
  438.                 typeIID = Components.interfaces.calIItemBase;
  439.             } else if (wantEvents) {
  440.                 typeIID = Components.interfaces.calIEvent;
  441.             } else if (wantTodos) {
  442.                 typeIID = Components.interfaces.calITodo;
  443.             }
  444.         }
  445.  
  446.         //  if aCount != 0, we don't attempt to sort anything, and
  447.         //  instead return the first aCount items that match.
  448.  
  449.         for (var itemIndex in this.mItems) {
  450.             var item = this.mItems[itemIndex];
  451.             var itemtoadd = null;
  452.  
  453.             var itemStartTime = 0;
  454.             var itemEndTime = 0;
  455.  
  456.             var tmpitem = item;
  457.             if (wantEvents && (item instanceof Components.interfaces.calIEvent)) {
  458.                 tmpitem = item.QueryInterface(Components.interfaces.calIEvent);
  459.                 itemStartTime = (item.startDate
  460.                                  ? item.startDate.nativeTime
  461.                                  : START_OF_TIME);
  462.                 itemEndTime = (item.endDate
  463.                                ? item.endDate.nativeTime
  464.                                : END_OF_TIME);
  465.             } else if (wantTodos && (item instanceof Components.interfaces.calITodo)) {
  466.                 // if it's a todo, also filter based on completeness
  467.                 if (item.isCompleted && !itemCompletedFilter)
  468.                     continue;
  469.                 else if (item.isCompleted && !itemNotCompletedFilter)
  470.                     continue;
  471.  
  472.                 itemStartTime = (item.entryDate
  473.                                  ? item.entryDate.nativeTime
  474.                                  : START_OF_TIME);
  475.                 itemEndTime = (item.dueDate
  476.                                ? item.dueDate.nativeTime
  477.                                : END_OF_TIME);
  478.             } else {
  479.                 // XXX unknown item type, wth do we do?
  480.                 continue;
  481.             }
  482.  
  483.             // Correct for floating
  484.             if (aRangeStart && item.startDate && item.startDate.timezone == 'floating')
  485.                 itemStartTime -= aRangeStart.timezoneOffset * USECS_PER_SECOND;
  486.             if (aRangeEnd && item.endDate && item.endDate.timezone == 'floating')
  487.                 itemEndTime -= aRangeEnd.timezoneOffset * USECS_PER_SECOND;
  488.  
  489.             if (itemStartTime < endTime) {
  490.                 // figure out if there are recurrences here we care about
  491.                 if (itemReturnOccurrences && item.recurrenceInfo)
  492.                 {
  493.                     // there might be some recurrences here that we need to handle
  494.                     var recs = item.recurrenceInfo.getOccurrences (aRangeStart, aRangeEnd, 0, {});
  495.                     itemsFound = itemsFound.concat(recs);
  496.                 } else if (itemEndTime >= startTime) {
  497.                     itemsFound.push(item);
  498.                 }
  499.             }
  500.  
  501.             if (aCount && itemsFound.length >= aCount)
  502.                 break;
  503.         }
  504.  
  505.         aListener.onGetResult (this.calendarToReturn,
  506.                                Components.results.NS_OK,
  507.                                typeIID,
  508.                                null,
  509.                                itemsFound.length,
  510.                                itemsFound);
  511.  
  512.         aListener.onOperationComplete (this.calendarToReturn,
  513.                                        Components.results.NS_OK,
  514.                                        aListener.GET,
  515.                                        null,
  516.                                        null);
  517.     },
  518.  
  519.     startBatch: function ()
  520.     {
  521.         this.mObservers.notify("onStartBatch");
  522.     },
  523.     endBatch: function ()
  524.     {
  525.         this.mObservers.notify("onEndBatch");
  526.     }
  527. }
  528.  
  529. /****
  530.  **** module registration
  531.  ****/
  532.  
  533. var calMemoryCalendarModule = {
  534.     mCID: Components.ID("{bda0dd7f-0a2f-4fcf-ba08-5517e6fbf133}"),
  535.     mContractID: "@mozilla.org/calendar/calendar;1?type=memory",
  536.  
  537.     mUtilsLoaded: false,
  538.     loadUtils: function memoryLoadUtils() {
  539.         if (this.mUtilsLoaded)
  540.             return;
  541.  
  542.         const jssslContractID = "@mozilla.org/moz/jssubscript-loader;1";
  543.         const jssslIID = Components.interfaces.mozIJSSubScriptLoader;
  544.  
  545.         const iosvcContractID = "@mozilla.org/network/io-service;1";
  546.         const iosvcIID = Components.interfaces.nsIIOService;
  547.  
  548.         var loader = Components.classes[jssslContractID].getService(jssslIID);
  549.         var iosvc = Components.classes[iosvcContractID].getService(iosvcIID);
  550.  
  551.         // Note that unintuitively, __LOCATION__.parent == .
  552.         // We expect to find utils in ./../js
  553.         var appdir = __LOCATION__.parent.parent;
  554.         appdir.append("js");
  555.         var scriptName = "calUtils.js";
  556.  
  557.         var f = appdir.clone();
  558.         f.append(scriptName);
  559.  
  560.         try {
  561.             var fileurl = iosvc.newFileURI(f);
  562.             loader.loadSubScript(fileurl.spec, this.__parent__.__parent__);
  563.         } catch (e) {
  564.             dump("Error while loading " + fileurl.spec + "\n");
  565.             throw e;
  566.         }
  567.  
  568.         this.mUtilsLoaded = true;
  569.     },
  570.  
  571.     
  572.     registerSelf: function (compMgr, fileSpec, location, type) {
  573.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  574.         compMgr.registerFactoryLocation(this.mCID,
  575.                                         "Calendar in-memory back-end",
  576.                                         this.mContractID,
  577.                                         fileSpec,
  578.                                         location,
  579.                                         type);
  580.     },
  581.  
  582.     getClassObject: function (compMgr, cid, iid) {
  583.         if (!cid.equals(this.mCID))
  584.             throw Components.results.NS_ERROR_NO_INTERFACE;
  585.  
  586.         if (!iid.equals(Components.interfaces.nsIFactory))
  587.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  588.  
  589.         this.loadUtils();
  590.  
  591.         return this.mFactory;
  592.     },
  593.  
  594.     mFactory: {
  595.         createInstance: function (outer, iid) {
  596.             if (outer != null)
  597.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  598.             return (new calMemoryCalendar()).QueryInterface(iid);
  599.         }
  600.     },
  601.  
  602.     canUnload: function(compMgr) {
  603.         return true;
  604.     }
  605. };
  606.  
  607. function NSGetModule(compMgr, fileSpec) {
  608.     return calMemoryCalendarModule;
  609. }
  610.